library(tidyverse)         # for graphing and data cleaning
library(tidymodels)        # for modeling
library(themis)            # for step functions for unbalanced data
library(doParallel)        # for parallel processing
library(stacks)            # for stacking models
library(naniar)            # for examining missing values (NAs)
library(lubridate)         # for date manipulation
library(moderndive)        # for King County housing data
library(vip)               # for variable importance plots
library(patchwork)         # for combining plots nicely
theme_set(theme_minimal()) # Lisa's favorite theme
data("lending_club")
# Data dictionary (as close as I could find): https://www.kaggle.com/wordsforthewise/lending-club/discussion/170691

When you finish the assignment, remove the # from the options chunk at the top, so that messages and warnings aren’t printed. If you are getting errors in your code, add error = TRUE so that the file knits. I would recommend not removing the # until you are completely finished.

Put it on GitHub!

From now on, GitHub should be part of your routine when doing assignments. I recommend making it part of your process anytime you are working in R, but I’ll make you show it’s part of your process for assignments.

Task: When you are finished with the assignment, post a link below to the GitHub repo for the assignment. Make sure the link goes to a spot in the repo where I can easily find this assignment. For example, if you have a website with a blog and post the assignment as a blog post, link to the post’s folder in the repo. As an example, I’ve linked to my GitHub stacking material here.

LINK to Cande’s repository: https://github.com/MCTJ1998/ADS_Assignment.git

Modeling

We’ll be using the lending_club dataset from the modeldata library, which is part of tidymodels. The data dictionary they reference doesn’t seem to exist anymore, but it seems the one on this kaggle discussion is pretty close. It might also help to read a bit about Lending Club before starting in on the exercises.

The outcome we are interested in predicting is Class. And according to the dataset’s help page, its values are “either ‘good’ (meaning that the loan was fully paid back or currently on-time) or ‘bad’ (charged off, defaulted, or 21-120 days late)”.

Tasks:

  1. Explore the data, concentrating on examining distributions of variables and examining missing values.
lending_club %>% 
  count(Class)
lending_club %>% 
  select(where(is.numeric)) %>% 
  pivot_longer(cols = everything(),
               names_to = "variable", 
               values_to = "value") %>% 
  ggplot(aes(x = value)) +
  geom_histogram(bins = 30) +
  facet_wrap(vars(variable), 
             scales = "free")

lending_mod <- lending_club %>% 
  mutate(Class = as.factor(Class)) %>% 
  mutate(across(where(is.character), as.factor)) %>% 
  select(-funded_amnt,
         -verification_status,
         -annual_inc) %>% 
  add_n_miss() %>% 
  filter(n_miss_all == 0) %>% 
  select(-n_miss_all)
lending_mod %>% 
  select(where(is.factor)) %>% 
  pivot_longer(cols = everything(),
               names_to = "variable", 
               values_to = "value") %>% 
  ggplot(aes(x = value)) +
  geom_bar() +
  facet_wrap(vars(variable), 
             scales = "free", 
             nrow = 2)

  1. Split the data into training and test, putting 75% in the training data. Stratify by Class (add strata =Classto theinitial_split()` function).
set.seed(494) # for reproducibility

lending_split <- initial_split(lending_club, 
                               strata= 'Class',
                               prop = .75)

lending_training <- training(lending_split)
lending_test <- testing(lending_split)
  1. Set up the recipe and the pre-processing steps to build a lasso model. Some steps you should take:
  • Use step_upsample() from the themis library to upsample the “bad” category so that it is 50% of the “good” category. Do this by setting over_ratio = .5.
  • Use step_downsample() from the themis library to downsample the “good” category so the bads and goods are even - set under_ratio = 1. Make sure to do this step AFTER step_upsample().
  • Make all integer variables numeric (I’d highly recommend using step_mutate_at() and using the all_numeric() helper or this will be a lot of code). This step might seem really weird right now, but we’ll want to do this for the model interpretation we’ll do in a later assignment.
  • Think about grouping factor variables with many levels.
  • Make categorical variables dummy variables (make sure NOT to do this to the outcome variable).
  • Normalize quantitative variables.

Once you have that, use prep(), juice(), and count() to count the number of observations in each class. They should be equal. This dataset will be used in building the model, but the data without up and down sampling will be used in evaluation.

set.seed(456)

lasso_recipe <- recipe(Class ~ .,
                       data=lending_training) %>% 

  #Pre-processing:
  step_upsample(Class, over_ratio = 0.5) %>% #This function creates a specification that will replicate rows of a data set to make the occurrence of levels in a specific factor level equal.
  step_downsample(Class, under_ratio = 1) %>% 
  step_mutate_at(all_numeric(),
                 fn= ~as.numeric(.)) %>% 
  #step_novel(all_nominal_predictors()) %>% 
  step_nzv(-all_outcomes()) %>% 
  step_dummy(all_nominal_predictors()) %>% 
  step_normalize(all_numeric_predictors())
lasso_recipe %>% 
  prep(lending_training) %>%
  # using bake(new_data = NULL) gives same result as juice()
  # bake(new_data = NULL)
  juice() 
  1. Set up the lasso model and workflow. We will tune the penalty parameter.
lasso_mod <- logistic_reg(penalty =tune()) %>%
  set_engine("glmnet") %>% 
  set_mode("classification")

lasso_wf <- workflow() %>% 
  add_recipe(lasso_recipe) %>% 
  add_model(lasso_mod)
  1. Set up the model tuning for the penalty parameter. Be sure to add the control_stack_grid() for the control argument so we can use these results later when we stack. Find the accuracy and area under the roc curve for the model with the best tuning parameter. Use 5-fold cv.

Tune:

set.seed(494) #for reproducible 5-fold
lending_cv <- vfold_cv(lending_training, v = 5)

# penalty grid - changed to 10 levels
penalty_grid <- grid_regular(penalty(),
                             levels = 10)

# add ctrl_grid - assures predictions and workflows are saved
ctrl_grid <- control_stack_grid()
set.seed(494) # for reproducibility

# tune the model 
lasso_tune <- 
  lasso_wf %>% 
  tune_grid(
    resamples = lending_cv,
    grid = penalty_grid,
    control = ctrl_grid)
 #Best tuning parameter ROC

lending_best_p_roc<- 
  select_best(lasso_tune,
              metric = 'roc_auc')
lending_best_p_roc
 #Best tuning parameter Accuracy

lending_best_p_ac<- 
  select_best(lasso_tune,
              metric = 'accuracy')
lending_best_p_ac
lending_final_wf <- 
  lasso_wf %>% 
  finalize_workflow (lending_best_p_roc)

lending_final_wf
## ══ Workflow ════════════════════════════════════════════════════════════════════
## Preprocessor: Recipe
## Model: logistic_reg()
## 
## ── Preprocessor ────────────────────────────────────────────────────────────────
## 6 Recipe Steps
## 
## • step_upsample()
## • step_downsample()
## • step_mutate_at()
## • step_nzv()
## • step_dummy()
## • step_normalize()
## 
## ── Model ───────────────────────────────────────────────────────────────────────
## Logistic Regression Model Specification (classification)
## 
## Main Arguments:
##   penalty = 0.0774263682681128
## 
## Computational engine: glmnet
lending_fit <- lending_final_wf %>%
fit(data = lending_training)
  1. Set up the recipe and the pre-processing steps to build a random forest model. You shouldn’t have to do as many steps. The only steps you should need to do are making all integers numeric and the up and down sampling.
set.seed(456)
rf_recipe  <- recipe(Class ~ ., 
                     data=lending_training) %>% 

  step_upsample(Class, over_ratio = 0.5) %>% 
  step_downsample(Class, under_ratio = 1) %>% 
  step_mutate_at(all_numeric(),
                 fn= ~as.numeric(.))
  1. Set up the random forest model and workflow. We will tune the mtry and min_n parameters and set the number of trees, trees, to 100 (otherwise the next steps take too long).
rf_model <-  
  rand_forest(mtry = tune(), 
              min_n = tune(), 
              trees = 100) %>% 
  set_mode("classification") %>% 
  set_engine("ranger")

rf_workflow <-
 workflow() %>% 
  add_recipe(rf_recipe) %>% 
  add_model(rf_model) 
  1. Set up the model tuning for both the mtry and min_n parameters. Be sure to add the control_stack_grid() for the control argument so we can use these results later when we stack. Use only 3 levels in the grid. For the mtry parameter, you need to put finalize(mtry(), lending_training %>% select(-Class)) in as an argument instead of just mtry(), where lending_training is the name of your training data. This is because the mtry() grid will otherwise have unknowns in it. This part can take a while to run.
set.seed(494)

# penalty grid - changed to 10 levels
rf_penalty_grid <- grid_regular(finalize(mtry(), lending_training %>% select(-Class)), min_n(),
                             levels = 3)

# add ctrl_grid - assures predictions and workflows are saved
rf_ctrl_grid <- control_stack_grid()
set.seed(454)
rf_tune <-
  rf_workflow %>% 
  tune_grid(
    resamples = lending_cv,
    grid = rf_penalty_grid,
    control = rf_ctrl_grid)
  1. Find the best tuning parameters. What are the accuracy and area under the ROC curve for the model with those tuning parameters?
#Best tuning parameter ROC

rf_best_p_roc<- 
  select_best(rf_tune,
              metric = 'roc_auc')
rf_best_p_roc
#Best tuning parameter Accuracy

rf_best_p_ac<- 
  select_best(rf_tune,
              metric = 'accuracy')
rf_best_p_ac
  • In contrast with the LASSO metrics, here we can see that the best tunning parameters differ for roc_auc and for accuracy. For roc_auc we have mtry=1 while for accuracy that is mtry=11. In terms of min_n we have 40 for roc_auc and 2 for accuracy.
  1. Next, we will fit a boosted tree using xgboost. We will only tune the learn_rate parameter. I have specified the model, recipe, and workflow below already (uncomment the code - you can this by highlighting it and then in the code tab at the top, choose comment/uncomment lines). You need to set up a grid of ten values for the tuning parameter and tune the model. Be sure to add the control_stack_grid() for the control argument so we can use these results later when we stack.
xgboost_spec <- 
   boost_tree(
     trees = 1000, 
     min_n = 5, 
     tree_depth = 2, 
     learn_rate = tune(), 
     loss_reduction = 10^-5, 
     sample_size = 1) %>% 
   set_mode("classification") %>% 
   set_engine("xgboost") 
 
 xgboost_recipe <- recipe(formula = Class ~ ., data = lending_training) %>% 
   step_upsample(Class, over_ratio = .5) %>% 
   step_downsample(Class, under_ratio = 1) %>% 
   step_mutate_at(all_numeric(), 
                 fn = ~as.numeric(.)) %>% 
   step_novel(all_nominal_predictors()) %>% 
   step_dummy(all_nominal_predictors(), one_hot = TRUE) %>% 
   step_zv(all_predictors()) 
 
 xgboost_workflow <- 
   workflow() %>% 
   add_recipe(xgboost_recipe) %>% 
   add_model(xgboost_spec) 
boost_grid <- grid_regular(learn_rate(),
                           levels = 10)
boost_grid
set.seed(494)
registerDoParallel() 
boost_tune <- tune_grid(
  xgboost_workflow, 
  lending_cv,
  grid = boost_grid,
  control = ctrl_grid
)
  1. Find the best tuning parameters. What are the accuracy and area under the ROC curve for the model with those tuning parameters?
#Best tuning parameter ROC

boost_best_p_roc<- 
  select_best(boost_tune,
              metric = 'roc_auc')
boost_best_p_roc
#Best tuning parameter Accuracy

boost_best_p_ac<- 
  select_best(boost_tune,
              metric = 'accuracy')
boost_best_p_ac
  1. Create a model stack with the candidate models from the previous parts of the exercise and use the blend_predictions() function to find the coefficients of the stacked model.
lending_stack <- 
 stacks() %>% 
  add_candidates(boost_tune) %>% 
  add_candidates(lasso_tune) %>% 
  add_candidates(rf_tune)

as_tibble(lending_stack)
lending_blend <-
  lending_stack %>% 
  blend_predictions()

type=prob #give predicted prbabilites

  • Create a plot examining the performance metrics for the different penalty parameters to assure you have captured the best one. If not, adjust the penalty. (HINT: use the autoplot() function).
autoplot(lending_blend)

autoplot(lending_blend, type= 'weights')

- Which models are contributing most? The one that contributes the most seems to be the logistic regression, followed by the random forest.

  1. Fit the final stacked model using fit_members().
lending_final_stack <- lending_blend %>% 
  fit_members()

lending_final_stack
## # A tibble: 5 × 3
##   member                     type         weight
##   <chr>                      <chr>         <dbl>
## 1 .pred_good_lasso_tune_1_09 logistic_reg  2.99 
## 2 .pred_good_rf_tune_1_7     rand_forest   1.47 
## 3 .pred_good_rf_tune_1_4     rand_forest   0.472
## 4 .pred_good_boost_tune_1_09 boost_tree    0.369
## 5 .pred_good_rf_tune_1_8     rand_forest   0.269
  • Apply the model to the training data.
#lending_fit <- lending_final_stack %>%
#fit(data = lending_training)
preds_accuracy_train <-lending_training %>% 
  bind_cols(predict(lending_final_stack, 
            new_data= .)) %>% 
  bind_cols(predict(lending_final_stack, 
                    new_data = .,
                    type = "prob"))



#preds<-collect_predictions(preds_accuracy_train) 
#Confusion Matrix 
lending_mat<-preds_accuracy_train %>%
  conf_mat(Class, .pred_class) 

 
lending_mat 
##           Truth
## Prediction  bad good
##       bad     0    0
##       good  383 7009
summary(lending_mat)

-Compute the accuracy, construct a confusion matrix, and create a density plot with .pred_good on the x-axis (the probability of a response of “good”), filled by Class. Comment on what you see.

preds_accuracy_train%>%
  ggplot(aes(x = .pred_good, fill = Class))+
  geom_density(alpha = 0.5, color = NA)

Porcentaje del tiempo que predice un case como bueno vs. el porcentaje del timepo que predice un case como malo.

  1. In the previous problem, you saw that although the accuracy was quite high, the true negative rate (aka sensitivity) was terrible. It’s common to see this when one of the classes has low representation. What we want to do now is investigate what happens in each of our models. Below I’ve provided code to investigate the lasso model (where lasso_tune is the name of my tuning step). Do similar things for the random forest and xgboost models.
lasso_tune %>% 
  collect_predictions() %>% 
  group_by(id, penalty) %>% 
  summarize(accuracy = sum((Class == .pred_class))/n(),
            true_neg_rate = sum(Class == "bad" & .pred_class == "bad")/sum(Class == "bad"),
            true_pos_rate = sum(Class == "good" & .pred_class == "good")/sum(Class == "good")) %>% 
  group_by(penalty) %>% 
  summarize(across(accuracy:true_pos_rate, mean))
rf_tune %>% 
  collect_predictions() %>% 
  group_by(id) %>% 
  summarize(accuracy = sum((Class == .pred_class))/n(),
            true_neg_rate = sum(Class == "bad" & .pred_class == "bad")/sum(Class == "bad"),
            true_pos_rate = sum(Class == "good" & .pred_class == "good")/sum(Class == "good")) %>% 
  summarize(across(accuracy:true_pos_rate, mean))
boost_tune %>% 
  collect_predictions() %>% 
  group_by(id) %>% 
  summarize(accuracy = sum((Class == .pred_class))/n(),
            true_neg_rate = sum(Class == "bad" & .pred_class == "bad")/sum(Class == "bad"),
            true_pos_rate = sum(Class == "good" & .pred_class == "good")/sum(Class == "good")) %>% 
  summarize(across(accuracy:true_pos_rate, mean))

LASSO has 72% accuracy, lambda=7.7, true negative rate= 63%

If you’d like to have a better true negative rate, which models would you choose and how would you go about doing this in a less manual way (you don’t need to write code to do it - just describe it in words). Be sure to remove the eval=FALSE when you are finished.

Shiny app

For this week, there is no code to turn in for this part. You are just going to need to think about the steps to take.

If you are new to Shiny apps or it’s been awhile since you’ve made one, visit the Shiny links on our course Resource page. I would recommend starting with my resource because it will be the most basic.

Everyone should watch the Theming Shiny talk by Carson Sievert so you can make your app look amazing.

Tasks:

In the future, you are going to create an app that allows a user to explore how the predicted probability of a loan being paid back (or maybe just the predicted class - either “good” or “bad”) changes depending on the values of the predictor variables.

For this week, I want you to answer the following questions:

  1. How can you save a model you built to use it later (like in the shiny app you’ll create)?

We will use the following code save(my_model , file = ‘StackModel.rda’)

  1. For shiny apps that get published (like yours will), it’s very important to have ALL the libraries that are used within the app loaded. If we were going to use the stacked model, which libraries do you think we’d need to load in our app?

library(tidyverse), library(tidymodels), library(themis), library(doParallel), library(stacks), library(naniar), library(lubridate), library(moderndive), library(vip), library(patchwork), theme_set(theme_minimal())

  1. You’ll want the user to be able to choose values for each variable in the model. How will you come up with the values they can choose for quantitative and categorical data? Give one example for each, either using code or in words.

For each predictor I would set a number or text input where people can choose their values.

ui <- fluidPage( textInput( “verification_status”, “Status”, value = "“, placeholder =”Choose your status"),

Then I would add a submit button.

I would do the same thing but with numeric inputs. Maybe add a square where users can type the number they want for continuous variables.

  1. You will need to populate each variable with an initial value. Which value will you choose? Is there a nice way to do this programatically (ie. with code)?

I will work on this with Lisa during office hours because I genuinely don’t know.

Function Friday problems

I will link to these separately. They will be posted by Tuesday.

Coded Bias

We will be watching some of the Coded Bias film together on Thursday. It is streaming on Netflix. Write a short reflection. If you want some prompts, reflect on: What part of the film impacted you the most? Was there a part that surprised you and why? What emotions did you experience while watching?

After forcing my housemates to watch the documentary with me over dinner, I think what impacted us the most was imagining the possibility of living in a state of full surveillance. We talked about this, and even though we know that we are being tracked and spied over the consumption we make online or the content we watch, this is done low-key and we are able to ignore it, or just not think about it. If we imagine our faces being tracked in the streets, like it happens in London or in China, it gets a bit more real and all of the sudden it is scary. It is also very scary to feel that algorithms control our lives and the way we make decisions that affect the lives and destinies of millions of people around the world. From hiring, to credit loans, to housing opportunities, these algorithms are behind many companies that control the way we live in society and they are embedded with the ideas and ideologies of the person that created it and the data it was fed with. I love how the documentary exposes the many biases behind the algorithms and how they denounce the “algorithmic harm” as a power structure where we (normal people) cannot appeal to the decisions made that harm us. I totally agree with what was said about the fact that we need to be constantly monitoring our use of algorithms. The Big Brother Watch group in London attempts to do so, but there should be a regulatory body controlling how big companies use algorithms in their activities, as well. The systemic biases that already exist in our world, get translated to the technology governing our lives and that is something really worrisome to me.

REMEMBER TO ADD YOUR GITHUB LINK AT THE TOP OF THE PAGE AND UNCOMMENT THE knitr OPTIONS.

LS0tCnRpdGxlOiAnQXNzaWdubWVudCAjMycKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UpCmBgYAoKYGBge3IgbGlicmFyaWVzfQpsaWJyYXJ5KHRpZHl2ZXJzZSkgICAgICAgICAjIGZvciBncmFwaGluZyBhbmQgZGF0YSBjbGVhbmluZwpsaWJyYXJ5KHRpZHltb2RlbHMpICAgICAgICAjIGZvciBtb2RlbGluZwpsaWJyYXJ5KHRoZW1pcykgICAgICAgICAgICAjIGZvciBzdGVwIGZ1bmN0aW9ucyBmb3IgdW5iYWxhbmNlZCBkYXRhCmxpYnJhcnkoZG9QYXJhbGxlbCkgICAgICAgICMgZm9yIHBhcmFsbGVsIHByb2Nlc3NpbmcKbGlicmFyeShzdGFja3MpICAgICAgICAgICAgIyBmb3Igc3RhY2tpbmcgbW9kZWxzCmxpYnJhcnkobmFuaWFyKSAgICAgICAgICAgICMgZm9yIGV4YW1pbmluZyBtaXNzaW5nIHZhbHVlcyAoTkFzKQpsaWJyYXJ5KGx1YnJpZGF0ZSkgICAgICAgICAjIGZvciBkYXRlIG1hbmlwdWxhdGlvbgpsaWJyYXJ5KG1vZGVybmRpdmUpICAgICAgICAjIGZvciBLaW5nIENvdW50eSBob3VzaW5nIGRhdGEKbGlicmFyeSh2aXApICAgICAgICAgICAgICAgIyBmb3IgdmFyaWFibGUgaW1wb3J0YW5jZSBwbG90cwpsaWJyYXJ5KHBhdGNod29yaykgICAgICAgICAjIGZvciBjb21iaW5pbmcgcGxvdHMgbmljZWx5CnRoZW1lX3NldCh0aGVtZV9taW5pbWFsKCkpICMgTGlzYSdzIGZhdm9yaXRlIHRoZW1lCmBgYAoKYGBge3IgZGF0YX0KZGF0YSgibGVuZGluZ19jbHViIikKIyBEYXRhIGRpY3Rpb25hcnkgKGFzIGNsb3NlIGFzIEkgY291bGQgZmluZCk6IGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vd29yZHNmb3J0aGV3aXNlL2xlbmRpbmctY2x1Yi9kaXNjdXNzaW9uLzE3MDY5MQpgYGAKCgpXaGVuIHlvdSBmaW5pc2ggdGhlIGFzc2lnbm1lbnQsIHJlbW92ZSB0aGUgYCNgIGZyb20gdGhlIG9wdGlvbnMgY2h1bmsgYXQgdGhlIHRvcCwgc28gdGhhdCBtZXNzYWdlcyBhbmQgd2FybmluZ3MgYXJlbid0IHByaW50ZWQuIElmIHlvdSBhcmUgZ2V0dGluZyBlcnJvcnMgaW4geW91ciBjb2RlLCBhZGQgYGVycm9yID0gVFJVRWAgc28gdGhhdCB0aGUgZmlsZSBrbml0cy4gSSB3b3VsZCByZWNvbW1lbmQgbm90IHJlbW92aW5nIHRoZSBgI2AgdW50aWwgeW91IGFyZSBjb21wbGV0ZWx5IGZpbmlzaGVkLgoKIyMgUHV0IGl0IG9uIEdpdEh1YiEgICAgICAgIAoKRnJvbSBub3cgb24sIEdpdEh1YiBzaG91bGQgYmUgcGFydCBvZiB5b3VyIHJvdXRpbmUgd2hlbiBkb2luZyBhc3NpZ25tZW50cy4gSSByZWNvbW1lbmQgbWFraW5nIGl0IHBhcnQgb2YgeW91ciBwcm9jZXNzIGFueXRpbWUgeW91IGFyZSB3b3JraW5nIGluIFIsIGJ1dCBJJ2xsIG1ha2UgeW91IHNob3cgaXQncyBwYXJ0IG9mIHlvdXIgcHJvY2VzcyBmb3IgYXNzaWdubWVudHMuCgoqKlRhc2sqKjogV2hlbiB5b3UgYXJlIGZpbmlzaGVkIHdpdGggdGhlIGFzc2lnbm1lbnQsIHBvc3QgYSBsaW5rIGJlbG93IHRvIHRoZSBHaXRIdWIgcmVwbyBmb3IgdGhlIGFzc2lnbm1lbnQuIE1ha2Ugc3VyZSB0aGUgbGluayBnb2VzIHRvIGEgc3BvdCBpbiB0aGUgcmVwbyB3aGVyZSBJIGNhbiBlYXNpbHkgZmluZCB0aGlzIGFzc2lnbm1lbnQuIEZvciBleGFtcGxlLCBpZiB5b3UgaGF2ZSBhIHdlYnNpdGUgd2l0aCBhIGJsb2cgYW5kIHBvc3QgdGhlIGFzc2lnbm1lbnQgYXMgYSBibG9nIHBvc3QsIGxpbmsgdG8gdGhlIHBvc3QncyBmb2xkZXIgaW4gdGhlIHJlcG8uIEFzIGFuIGV4YW1wbGUsIEkndmUgbGlua2VkIHRvIG15IEdpdEh1YiBzdGFja2luZyBtYXRlcmlhbCBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2xsZW5kd2F5L2Fkc193ZWJzaXRlL3RyZWUvbWFzdGVyL19wb3N0cy8yMDIxLTAzLTIyLXN0YWNraW5nKS4KCkxJTksgdG8gQ2FuZGUncyByZXBvc2l0b3J5OiBodHRwczovL2dpdGh1Yi5jb20vTUNUSjE5OTgvQURTX0Fzc2lnbm1lbnQuZ2l0CgojIyBNb2RlbGluZwoKV2UnbGwgYmUgdXNpbmcgdGhlIGBsZW5kaW5nX2NsdWJgIGRhdGFzZXQgZnJvbSB0aGUgYG1vZGVsZGF0YWAgbGlicmFyeSwgd2hpY2ggaXMgcGFydCBvZiBgdGlkeW1vZGVsc2AuIFRoZSBkYXRhIGRpY3Rpb25hcnkgdGhleSByZWZlcmVuY2UgZG9lc24ndCBzZWVtIHRvIGV4aXN0IGFueW1vcmUsIGJ1dCBpdCBzZWVtcyB0aGUgb25lIG9uIHRoaXMgW2thZ2dsZSBkaXNjdXNzaW9uXShodHRwczovL3d3dy5rYWdnbGUuY29tL3dvcmRzZm9ydGhld2lzZS9sZW5kaW5nLWNsdWIvZGlzY3Vzc2lvbi8xNzA2OTEpIGlzIHByZXR0eSBjbG9zZS4gSXQgbWlnaHQgYWxzbyBoZWxwIHRvIHJlYWQgYSBiaXQgYWJvdXQgW0xlbmRpbmcgQ2x1Yl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTGVuZGluZ0NsdWIpIGJlZm9yZSBzdGFydGluZyBpbiBvbiB0aGUgZXhlcmNpc2VzLgoKVGhlIG91dGNvbWUgd2UgYXJlIGludGVyZXN0ZWQgaW4gcHJlZGljdGluZyBpcyBgQ2xhc3NgLiBBbmQgYWNjb3JkaW5nIHRvIHRoZSBkYXRhc2V0J3MgaGVscCBwYWdlLCBpdHMgdmFsdWVzIGFyZSAiZWl0aGVyICdnb29kJyAobWVhbmluZyB0aGF0IHRoZSBsb2FuIHdhcyBmdWxseSBwYWlkIGJhY2sgb3IgY3VycmVudGx5IG9uLXRpbWUpIG9yICdiYWQnIChjaGFyZ2VkIG9mZiwgZGVmYXVsdGVkLCBvciAyMS0xMjAgZGF5cyBsYXRlKSIuCgoqKlRhc2tzOioqIAoKMS4gRXhwbG9yZSB0aGUgZGF0YSwgY29uY2VudHJhdGluZyBvbiBleGFtaW5pbmcgZGlzdHJpYnV0aW9ucyBvZiB2YXJpYWJsZXMgYW5kIGV4YW1pbmluZyBtaXNzaW5nIHZhbHVlcy4gCgpgYGB7cn0KbGVuZGluZ19jbHViICU+JSAKICBjb3VudChDbGFzcykKYGBgCgpgYGB7cn0KbGVuZGluZ19jbHViICU+JSAKICBzZWxlY3Qod2hlcmUoaXMubnVtZXJpYykpICU+JSAKICBwaXZvdF9sb25nZXIoY29scyA9IGV2ZXJ5dGhpbmcoKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAidmFyaWFibGUiLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInZhbHVlIikgJT4lIAogIGdncGxvdChhZXMoeCA9IHZhbHVlKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAzMCkgKwogIGZhY2V0X3dyYXAodmFycyh2YXJpYWJsZSksIAogICAgICAgICAgICAgc2NhbGVzID0gImZyZWUiKQpgYGAKYGBge3J9CmxlbmRpbmdfbW9kIDwtIGxlbmRpbmdfY2x1YiAlPiUgCiAgbXV0YXRlKENsYXNzID0gYXMuZmFjdG9yKENsYXNzKSkgJT4lIAogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMuY2hhcmFjdGVyKSwgYXMuZmFjdG9yKSkgJT4lIAogIHNlbGVjdCgtZnVuZGVkX2FtbnQsCiAgICAgICAgIC12ZXJpZmljYXRpb25fc3RhdHVzLAogICAgICAgICAtYW5udWFsX2luYykgJT4lIAogIGFkZF9uX21pc3MoKSAlPiUgCiAgZmlsdGVyKG5fbWlzc19hbGwgPT0gMCkgJT4lIAogIHNlbGVjdCgtbl9taXNzX2FsbCkKYGBgCgpgYGB7cn0KbGVuZGluZ19tb2QgJT4lIAogIHNlbGVjdCh3aGVyZShpcy5mYWN0b3IpKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBldmVyeXRoaW5nKCksCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gInZhcmlhYmxlIiwgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSkpICsKICBnZW9tX2JhcigpICsKICBmYWNldF93cmFwKHZhcnModmFyaWFibGUpLCAKICAgICAgICAgICAgIHNjYWxlcyA9ICJmcmVlIiwgCiAgICAgICAgICAgICBucm93ID0gMikKYGBgCgoyLiBTcGxpdCB0aGUgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0LCBwdXR0aW5nIDc1XCUgaW4gdGhlIHRyYWluaW5nIGRhdGEuIFN0cmF0aWZ5IGJ5IGBDbGFzc2AgKGFkZCBgc3RyYXRhID0gYENsYXNzYCB0byB0aGUgYGluaXRpYWxfc3BsaXQoKWAgZnVuY3Rpb24pLgoKYGBge3J9CnNldC5zZWVkKDQ5NCkgIyBmb3IgcmVwcm9kdWNpYmlsaXR5CgpsZW5kaW5nX3NwbGl0IDwtIGluaXRpYWxfc3BsaXQobGVuZGluZ19jbHViLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmF0YT0gJ0NsYXNzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb3AgPSAuNzUpCgpsZW5kaW5nX3RyYWluaW5nIDwtIHRyYWluaW5nKGxlbmRpbmdfc3BsaXQpCmxlbmRpbmdfdGVzdCA8LSB0ZXN0aW5nKGxlbmRpbmdfc3BsaXQpCmBgYAoKCjMuIFNldCB1cCB0aGUgcmVjaXBlIGFuZCB0aGUgcHJlLXByb2Nlc3Npbmcgc3RlcHMgdG8gYnVpbGQgYSBsYXNzbyBtb2RlbC4gU29tZSBzdGVwcyB5b3Ugc2hvdWxkIHRha2U6CgoqIFVzZSBgc3RlcF91cHNhbXBsZSgpYCBmcm9tIHRoZSBgdGhlbWlzYCBsaWJyYXJ5IHRvIHVwc2FtcGxlIHRoZSAiYmFkIiBjYXRlZ29yeSBzbyB0aGF0IGl0IGlzIDUwXCUgb2YgdGhlICJnb29kIiBjYXRlZ29yeS4gRG8gdGhpcyBieSBzZXR0aW5nIGBvdmVyX3JhdGlvID0gLjVgLgoqIFVzZSBgc3RlcF9kb3duc2FtcGxlKClgIGZyb20gdGhlIGB0aGVtaXNgIGxpYnJhcnkgdG8gZG93bnNhbXBsZSB0aGUgImdvb2QiIGNhdGVnb3J5IHNvIHRoZSBiYWRzIGFuZCBnb29kcyBhcmUgZXZlbiAtIHNldCBgdW5kZXJfcmF0aW8gPSAxYC4gTWFrZSBzdXJlIHRvIGRvIHRoaXMgc3RlcCBBRlRFUiBgc3RlcF91cHNhbXBsZSgpYC4gIAoqIE1ha2UgYWxsIGludGVnZXIgdmFyaWFibGVzIG51bWVyaWMgKEknZCBoaWdobHkgcmVjb21tZW5kIHVzaW5nIGBzdGVwX211dGF0ZV9hdCgpYCBhbmQgdXNpbmcgdGhlIGBhbGxfbnVtZXJpYygpYCBoZWxwZXIgb3IgdGhpcyB3aWxsIGJlIGEgbG90IG9mIGNvZGUpLiBUaGlzIHN0ZXAgbWlnaHQgc2VlbSByZWFsbHkgd2VpcmQgcmlnaHQgbm93LCBidXQgd2UnbGwgd2FudCB0byBkbyB0aGlzIGZvciB0aGUgbW9kZWwgaW50ZXJwcmV0YXRpb24gd2UnbGwgZG8gaW4gYSBsYXRlciBhc3NpZ25tZW50LiAgCiogVGhpbmsgYWJvdXQgZ3JvdXBpbmcgZmFjdG9yIHZhcmlhYmxlcyB3aXRoIG1hbnkgbGV2ZWxzLiAgCiogTWFrZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgZHVtbXkgdmFyaWFibGVzIChtYWtlIHN1cmUgTk9UIHRvIGRvIHRoaXMgdG8gdGhlIG91dGNvbWUgdmFyaWFibGUpLiAgCiogTm9ybWFsaXplIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMuICAKCk9uY2UgeW91IGhhdmUgdGhhdCwgdXNlIGBwcmVwKClgLCBganVpY2UoKWAsIGFuZCBgY291bnQoKWAgdG8gY291bnQgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaW4gZWFjaCBjbGFzcy4gVGhleSBzaG91bGQgYmUgZXF1YWwuIFRoaXMgZGF0YXNldCB3aWxsIGJlIHVzZWQgaW4gYnVpbGRpbmcgdGhlIG1vZGVsLCBidXQgdGhlIGRhdGEgd2l0aG91dCB1cCBhbmQgZG93biBzYW1wbGluZyB3aWxsIGJlIHVzZWQgaW4gZXZhbHVhdGlvbi4KCmBgYHtyfQpzZXQuc2VlZCg0NTYpCgpsYXNzb19yZWNpcGUgPC0gcmVjaXBlKENsYXNzIH4gLiwKICAgICAgICAgICAgICAgICAgICAgICBkYXRhPWxlbmRpbmdfdHJhaW5pbmcpICU+JSAKCiAgI1ByZS1wcm9jZXNzaW5nOgogIHN0ZXBfdXBzYW1wbGUoQ2xhc3MsIG92ZXJfcmF0aW8gPSAwLjUpICU+JSAjVGhpcyBmdW5jdGlvbiBjcmVhdGVzIGEgc3BlY2lmaWNhdGlvbiB0aGF0IHdpbGwgcmVwbGljYXRlIHJvd3Mgb2YgYSBkYXRhIHNldCB0byBtYWtlIHRoZSBvY2N1cnJlbmNlIG9mIGxldmVscyBpbiBhIHNwZWNpZmljIGZhY3RvciBsZXZlbCBlcXVhbC4KICBzdGVwX2Rvd25zYW1wbGUoQ2xhc3MsIHVuZGVyX3JhdGlvID0gMSkgJT4lIAogIHN0ZXBfbXV0YXRlX2F0KGFsbF9udW1lcmljKCksCiAgICAgICAgICAgICAgICAgZm49IH5hcy5udW1lcmljKC4pKSAlPiUgCiAgI3N0ZXBfbm92ZWwoYWxsX25vbWluYWxfcHJlZGljdG9ycygpKSAlPiUgCiAgc3RlcF9uenYoLWFsbF9vdXRjb21lcygpKSAlPiUgCiAgc3RlcF9kdW1teShhbGxfbm9taW5hbF9wcmVkaWN0b3JzKCkpICU+JSAKICBzdGVwX25vcm1hbGl6ZShhbGxfbnVtZXJpY19wcmVkaWN0b3JzKCkpCgpgYGAKCmBgYHtyfQpsYXNzb19yZWNpcGUgJT4lIAogIHByZXAobGVuZGluZ190cmFpbmluZykgJT4lCiAgIyB1c2luZyBiYWtlKG5ld19kYXRhID0gTlVMTCkgZ2l2ZXMgc2FtZSByZXN1bHQgYXMganVpY2UoKQogICMgYmFrZShuZXdfZGF0YSA9IE5VTEwpCiAganVpY2UoKSAKYGBgCgo0LiBTZXQgdXAgdGhlIGxhc3NvIG1vZGVsIGFuZCB3b3JrZmxvdy4gV2Ugd2lsbCB0dW5lIHRoZSBgcGVuYWx0eWAgcGFyYW1ldGVyLgoKYGBge3J9Cmxhc3NvX21vZCA8LSBsb2dpc3RpY19yZWcocGVuYWx0eSA9dHVuZSgpKSAlPiUKICBzZXRfZW5naW5lKCJnbG1uZXQiKSAlPiUgCiAgc2V0X21vZGUoImNsYXNzaWZpY2F0aW9uIikKCmxhc3NvX3dmIDwtIHdvcmtmbG93KCkgJT4lIAogIGFkZF9yZWNpcGUobGFzc29fcmVjaXBlKSAlPiUgCiAgYWRkX21vZGVsKGxhc3NvX21vZCkKYGBgCgo1LiBTZXQgdXAgdGhlIG1vZGVsIHR1bmluZyBmb3IgdGhlIGBwZW5hbHR5YCBwYXJhbWV0ZXIuIEJlIHN1cmUgdG8gYWRkIHRoZSBgY29udHJvbF9zdGFja19ncmlkKClgIGZvciB0aGUgYGNvbnRyb2xgIGFyZ3VtZW50IHNvIHdlIGNhbiB1c2UgdGhlc2UgcmVzdWx0cyBsYXRlciB3aGVuIHdlIHN0YWNrLiBGaW5kIHRoZSBhY2N1cmFjeSBhbmQgYXJlYSB1bmRlciB0aGUgcm9jIGN1cnZlIGZvciB0aGUgbW9kZWwgd2l0aCB0aGUgYmVzdCB0dW5pbmcgcGFyYW1ldGVyLiAgVXNlIDUtZm9sZCBjdi4KClR1bmU6CgpgYGB7cn0Kc2V0LnNlZWQoNDk0KSAjZm9yIHJlcHJvZHVjaWJsZSA1LWZvbGQKbGVuZGluZ19jdiA8LSB2Zm9sZF9jdihsZW5kaW5nX3RyYWluaW5nLCB2ID0gNSkKCiMgcGVuYWx0eSBncmlkIC0gY2hhbmdlZCB0byAxMCBsZXZlbHMKcGVuYWx0eV9ncmlkIDwtIGdyaWRfcmVndWxhcihwZW5hbHR5KCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gMTApCgojIGFkZCBjdHJsX2dyaWQgLSBhc3N1cmVzIHByZWRpY3Rpb25zIGFuZCB3b3JrZmxvd3MgYXJlIHNhdmVkCmN0cmxfZ3JpZCA8LSBjb250cm9sX3N0YWNrX2dyaWQoKQoKYGBgCgpgYGB7cn0Kc2V0LnNlZWQoNDk0KSAjIGZvciByZXByb2R1Y2liaWxpdHkKCiMgdHVuZSB0aGUgbW9kZWwgCmxhc3NvX3R1bmUgPC0gCiAgbGFzc29fd2YgJT4lIAogIHR1bmVfZ3JpZCgKICAgIHJlc2FtcGxlcyA9IGxlbmRpbmdfY3YsCiAgICBncmlkID0gcGVuYWx0eV9ncmlkLAogICAgY29udHJvbCA9IGN0cmxfZ3JpZCkKYGBgCgpgYGB7cn0KICNCZXN0IHR1bmluZyBwYXJhbWV0ZXIgUk9DCgpsZW5kaW5nX2Jlc3RfcF9yb2M8LSAKICBzZWxlY3RfYmVzdChsYXNzb190dW5lLAogICAgICAgICAgICAgIG1ldHJpYyA9ICdyb2NfYXVjJykKbGVuZGluZ19iZXN0X3Bfcm9jCmBgYApgYGB7cn0KICNCZXN0IHR1bmluZyBwYXJhbWV0ZXIgQWNjdXJhY3kKCmxlbmRpbmdfYmVzdF9wX2FjPC0gCiAgc2VsZWN0X2Jlc3QobGFzc29fdHVuZSwKICAgICAgICAgICAgICBtZXRyaWMgPSAnYWNjdXJhY3knKQpsZW5kaW5nX2Jlc3RfcF9hYwpgYGAKCgpgYGB7cn0KbGVuZGluZ19maW5hbF93ZiA8LSAKICBsYXNzb193ZiAlPiUgCiAgZmluYWxpemVfd29ya2Zsb3cgKGxlbmRpbmdfYmVzdF9wX3JvYykKCmxlbmRpbmdfZmluYWxfd2YKYGBgCgpgYGB7cn0KbGVuZGluZ19maXQgPC0gbGVuZGluZ19maW5hbF93ZiAlPiUKZml0KGRhdGEgPSBsZW5kaW5nX3RyYWluaW5nKQpgYGAKCgo2LiBTZXQgdXAgdGhlIHJlY2lwZSBhbmQgdGhlIHByZS1wcm9jZXNzaW5nIHN0ZXBzIHRvIGJ1aWxkIGEgcmFuZG9tIGZvcmVzdCBtb2RlbC4gWW91IHNob3VsZG4ndCBoYXZlIHRvIGRvIGFzIG1hbnkgc3RlcHMuIFRoZSBvbmx5IHN0ZXBzIHlvdSBzaG91bGQgbmVlZCB0byBkbyBhcmUgbWFraW5nIGFsbCBpbnRlZ2VycyBudW1lcmljIGFuZCB0aGUgdXAgYW5kIGRvd24gc2FtcGxpbmcuIAoKYGBge3J9CnNldC5zZWVkKDQ1NikKcmZfcmVjaXBlICA8LSByZWNpcGUoQ2xhc3MgfiAuLCAKICAgICAgICAgICAgICAgICAgICAgZGF0YT1sZW5kaW5nX3RyYWluaW5nKSAlPiUgCgogIHN0ZXBfdXBzYW1wbGUoQ2xhc3MsIG92ZXJfcmF0aW8gPSAwLjUpICU+JSAKICBzdGVwX2Rvd25zYW1wbGUoQ2xhc3MsIHVuZGVyX3JhdGlvID0gMSkgJT4lIAogIHN0ZXBfbXV0YXRlX2F0KGFsbF9udW1lcmljKCksCiAgICAgICAgICAgICAgICAgZm49IH5hcy5udW1lcmljKC4pKQogIApgYGAKCgo3LiBTZXQgdXAgdGhlIHJhbmRvbSBmb3Jlc3QgbW9kZWwgYW5kIHdvcmtmbG93LiBXZSB3aWxsIHR1bmUgdGhlIGBtdHJ5YCBhbmQgYG1pbl9uYCBwYXJhbWV0ZXJzIGFuZCBzZXQgdGhlIG51bWJlciBvZiB0cmVlcywgYHRyZWVzYCwgdG8gMTAwIChvdGhlcndpc2UgdGhlIG5leHQgc3RlcHMgdGFrZSB0b28gbG9uZykuCgpgYGB7cn0KcmZfbW9kZWwgPC0gIAogIHJhbmRfZm9yZXN0KG10cnkgPSB0dW5lKCksIAogICAgICAgICAgICAgIG1pbl9uID0gdHVuZSgpLCAKICAgICAgICAgICAgICB0cmVlcyA9IDEwMCkgJT4lIAogIHNldF9tb2RlKCJjbGFzc2lmaWNhdGlvbiIpICU+JSAKICBzZXRfZW5naW5lKCJyYW5nZXIiKQoKcmZfd29ya2Zsb3cgPC0KIHdvcmtmbG93KCkgJT4lIAogIGFkZF9yZWNpcGUocmZfcmVjaXBlKSAlPiUgCiAgYWRkX21vZGVsKHJmX21vZGVsKSAKYGBgCgo4LiBTZXQgdXAgdGhlIG1vZGVsIHR1bmluZyBmb3IgYm90aCB0aGUgYG10cnlgIGFuZCBgbWluX25gIHBhcmFtZXRlcnMuIEJlIHN1cmUgdG8gYWRkIHRoZSBgY29udHJvbF9zdGFja19ncmlkKClgIGZvciB0aGUgYGNvbnRyb2xgIGFyZ3VtZW50IHNvIHdlIGNhbiB1c2UgdGhlc2UgcmVzdWx0cyBsYXRlciB3aGVuIHdlIHN0YWNrLiBVc2Ugb25seSAzIGxldmVscyBpbiB0aGUgZ3JpZC4gRm9yIHRoZSBgbXRyeWAgcGFyYW1ldGVyLCB5b3UgbmVlZCB0byBwdXQgYGZpbmFsaXplKG10cnkoKSwgbGVuZGluZ190cmFpbmluZyAlPiUgc2VsZWN0KC1DbGFzcykpYCBpbiBhcyBhbiBhcmd1bWVudCBpbnN0ZWFkIG9mIGp1c3QgYG10cnkoKWAsIHdoZXJlIGBsZW5kaW5nX3RyYWluaW5nYCBpcyB0aGUgbmFtZSBvZiB5b3VyIHRyYWluaW5nIGRhdGEuIFRoaXMgaXMgYmVjYXVzZSB0aGUgYG10cnkoKWAgZ3JpZCB3aWxsIG90aGVyd2lzZSBoYXZlIHVua25vd25zIGluIGl0LiBUaGlzIHBhcnQgY2FuIHRha2UgYSB3aGlsZSB0byBydW4uCgoKYGBge3J9CnNldC5zZWVkKDQ5NCkKCiMgcGVuYWx0eSBncmlkIC0gY2hhbmdlZCB0byAxMCBsZXZlbHMKcmZfcGVuYWx0eV9ncmlkIDwtIGdyaWRfcmVndWxhcihmaW5hbGl6ZShtdHJ5KCksIGxlbmRpbmdfdHJhaW5pbmcgJT4lIHNlbGVjdCgtQ2xhc3MpKSwgbWluX24oKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSAzKQoKIyBhZGQgY3RybF9ncmlkIC0gYXNzdXJlcyBwcmVkaWN0aW9ucyBhbmQgd29ya2Zsb3dzIGFyZSBzYXZlZApyZl9jdHJsX2dyaWQgPC0gY29udHJvbF9zdGFja19ncmlkKCkKCmBgYAoKYGBge3J9CnNldC5zZWVkKDQ1NCkKcmZfdHVuZSA8LQogIHJmX3dvcmtmbG93ICU+JSAKICB0dW5lX2dyaWQoCiAgICByZXNhbXBsZXMgPSBsZW5kaW5nX2N2LAogICAgZ3JpZCA9IHJmX3BlbmFsdHlfZ3JpZCwKICAgIGNvbnRyb2wgPSByZl9jdHJsX2dyaWQpCgpgYGAKCjkuIEZpbmQgdGhlIGJlc3QgdHVuaW5nIHBhcmFtZXRlcnMuIFdoYXQgYXJlIHRoZSBhY2N1cmFjeSBhbmQgYXJlYSB1bmRlciB0aGUgUk9DIGN1cnZlIGZvciB0aGUgbW9kZWwgd2l0aCB0aG9zZSB0dW5pbmcgcGFyYW1ldGVycz8KCmBgYHtyfQojQmVzdCB0dW5pbmcgcGFyYW1ldGVyIFJPQwoKcmZfYmVzdF9wX3JvYzwtIAogIHNlbGVjdF9iZXN0KHJmX3R1bmUsCiAgICAgICAgICAgICAgbWV0cmljID0gJ3JvY19hdWMnKQpyZl9iZXN0X3Bfcm9jCmBgYAoKYGBge3J9CiNCZXN0IHR1bmluZyBwYXJhbWV0ZXIgQWNjdXJhY3kKCnJmX2Jlc3RfcF9hYzwtIAogIHNlbGVjdF9iZXN0KHJmX3R1bmUsCiAgICAgICAgICAgICAgbWV0cmljID0gJ2FjY3VyYWN5JykKcmZfYmVzdF9wX2FjCmBgYAotIEluIGNvbnRyYXN0IHdpdGggdGhlIExBU1NPIG1ldHJpY3MsIGhlcmUgd2UgY2FuIHNlZSB0aGF0IHRoZSBiZXN0IHR1bm5pbmcgcGFyYW1ldGVycyBkaWZmZXIgZm9yIHJvY19hdWMgYW5kIGZvciBhY2N1cmFjeS4gRm9yIHJvY19hdWMgd2UgaGF2ZSBtdHJ5PTEgd2hpbGUgZm9yIGFjY3VyYWN5IHRoYXQgaXMgbXRyeT0xMS4gSW4gdGVybXMgb2YgbWluX24gd2UgaGF2ZSA0MCBmb3Igcm9jX2F1YyBhbmQgMiBmb3IgYWNjdXJhY3kuIAoKCjEwLiBOZXh0LCB3ZSB3aWxsIGZpdCBhIGJvb3N0ZWQgdHJlZSB1c2luZyB4Z2Jvb3N0LiBXZSB3aWxsIG9ubHkgdHVuZSB0aGUgYGxlYXJuX3JhdGVgIHBhcmFtZXRlci4gSSBoYXZlIHNwZWNpZmllZCB0aGUgbW9kZWwsIHJlY2lwZSwgYW5kIHdvcmtmbG93IGJlbG93IGFscmVhZHkgKHVuY29tbWVudCB0aGUgY29kZSAtIHlvdSBjYW4gdGhpcyBieSBoaWdobGlnaHRpbmcgaXQgYW5kIHRoZW4gaW4gdGhlIGNvZGUgdGFiIGF0IHRoZSB0b3AsIGNob29zZSBjb21tZW50L3VuY29tbWVudCBsaW5lcykuIFlvdSBuZWVkIHRvIHNldCB1cCBhIGdyaWQgb2YgdGVuIHZhbHVlcyBmb3IgdGhlIHR1bmluZyBwYXJhbWV0ZXIgYW5kIHR1bmUgdGhlIG1vZGVsLiBCZSBzdXJlIHRvIGFkZCB0aGUgYGNvbnRyb2xfc3RhY2tfZ3JpZCgpYCBmb3IgdGhlIGBjb250cm9sYCBhcmd1bWVudCBzbyB3ZSBjYW4gdXNlIHRoZXNlIHJlc3VsdHMgbGF0ZXIgd2hlbiB3ZSBzdGFjay4KCmBgYHtyfQp4Z2Jvb3N0X3NwZWMgPC0gCiAgIGJvb3N0X3RyZWUoCiAgICAgdHJlZXMgPSAxMDAwLCAKICAgICBtaW5fbiA9IDUsIAogICAgIHRyZWVfZGVwdGggPSAyLCAKICAgICBsZWFybl9yYXRlID0gdHVuZSgpLCAKICAgICBsb3NzX3JlZHVjdGlvbiA9IDEwXi01LCAKICAgICBzYW1wbGVfc2l6ZSA9IDEpICU+JSAKICAgc2V0X21vZGUoImNsYXNzaWZpY2F0aW9uIikgJT4lIAogICBzZXRfZW5naW5lKCJ4Z2Jvb3N0IikgCiAKIHhnYm9vc3RfcmVjaXBlIDwtIHJlY2lwZShmb3JtdWxhID0gQ2xhc3MgfiAuLCBkYXRhID0gbGVuZGluZ190cmFpbmluZykgJT4lIAogICBzdGVwX3Vwc2FtcGxlKENsYXNzLCBvdmVyX3JhdGlvID0gLjUpICU+JSAKICAgc3RlcF9kb3duc2FtcGxlKENsYXNzLCB1bmRlcl9yYXRpbyA9IDEpICU+JSAKICAgc3RlcF9tdXRhdGVfYXQoYWxsX251bWVyaWMoKSwgCiAgICAgICAgICAgICAgICAgZm4gPSB+YXMubnVtZXJpYyguKSkgJT4lIAogICBzdGVwX25vdmVsKGFsbF9ub21pbmFsX3ByZWRpY3RvcnMoKSkgJT4lIAogICBzdGVwX2R1bW15KGFsbF9ub21pbmFsX3ByZWRpY3RvcnMoKSwgb25lX2hvdCA9IFRSVUUpICU+JSAKICAgc3RlcF96dihhbGxfcHJlZGljdG9ycygpKSAKIAogeGdib29zdF93b3JrZmxvdyA8LSAKICAgd29ya2Zsb3coKSAlPiUgCiAgIGFkZF9yZWNpcGUoeGdib29zdF9yZWNpcGUpICU+JSAKICAgYWRkX21vZGVsKHhnYm9vc3Rfc3BlYykgCgpgYGAKCgpgYGB7cn0KYm9vc3RfZ3JpZCA8LSBncmlkX3JlZ3VsYXIobGVhcm5fcmF0ZSgpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSAxMCkKYm9vc3RfZ3JpZApgYGAKCmBgYHtyfQpzZXQuc2VlZCg0OTQpCnJlZ2lzdGVyRG9QYXJhbGxlbCgpIApib29zdF90dW5lIDwtIHR1bmVfZ3JpZCgKICB4Z2Jvb3N0X3dvcmtmbG93LCAKICBsZW5kaW5nX2N2LAogIGdyaWQgPSBib29zdF9ncmlkLAogIGNvbnRyb2wgPSBjdHJsX2dyaWQKKQoKYGBgCgoxMS4gRmluZCB0aGUgYmVzdCB0dW5pbmcgcGFyYW1ldGVycy4gV2hhdCBhcmUgdGhlIGFjY3VyYWN5IGFuZCBhcmVhIHVuZGVyIHRoZSBST0MgY3VydmUgZm9yIHRoZSBtb2RlbCB3aXRoIHRob3NlIHR1bmluZyBwYXJhbWV0ZXJzPwoKYGBge3J9CiNCZXN0IHR1bmluZyBwYXJhbWV0ZXIgUk9DCgpib29zdF9iZXN0X3Bfcm9jPC0gCiAgc2VsZWN0X2Jlc3QoYm9vc3RfdHVuZSwKICAgICAgICAgICAgICBtZXRyaWMgPSAncm9jX2F1YycpCmJvb3N0X2Jlc3RfcF9yb2MKYGBgCgpgYGB7cn0KI0Jlc3QgdHVuaW5nIHBhcmFtZXRlciBBY2N1cmFjeQoKYm9vc3RfYmVzdF9wX2FjPC0gCiAgc2VsZWN0X2Jlc3QoYm9vc3RfdHVuZSwKICAgICAgICAgICAgICBtZXRyaWMgPSAnYWNjdXJhY3knKQpib29zdF9iZXN0X3BfYWMKYGBgCgoKCjEyLiBDcmVhdGUgYSBtb2RlbCBzdGFjayB3aXRoIHRoZSBjYW5kaWRhdGUgbW9kZWxzIGZyb20gdGhlIHByZXZpb3VzIHBhcnRzIG9mIHRoZSBleGVyY2lzZSBhbmQgdXNlIHRoZSBgYmxlbmRfcHJlZGljdGlvbnMoKWAgZnVuY3Rpb24gdG8gZmluZCB0aGUgY29lZmZpY2llbnRzIG9mIHRoZSBzdGFja2VkIG1vZGVsLiAKCmBgYHtyfQpsZW5kaW5nX3N0YWNrIDwtIAogc3RhY2tzKCkgJT4lIAogIGFkZF9jYW5kaWRhdGVzKGJvb3N0X3R1bmUpICU+JSAKICBhZGRfY2FuZGlkYXRlcyhsYXNzb190dW5lKSAlPiUgCiAgYWRkX2NhbmRpZGF0ZXMocmZfdHVuZSkKCmFzX3RpYmJsZShsZW5kaW5nX3N0YWNrKQpgYGAKCmBgYHtyfQpsZW5kaW5nX2JsZW5kIDwtCiAgbGVuZGluZ19zdGFjayAlPiUgCiAgYmxlbmRfcHJlZGljdGlvbnMoKQpgYGAKCnR5cGU9cHJvYiAKI2dpdmUgcHJlZGljdGVkIHByYmFiaWxpdGVzCgotIENyZWF0ZSBhIHBsb3QgZXhhbWluaW5nIHRoZSBwZXJmb3JtYW5jZSBtZXRyaWNzIGZvciB0aGUgZGlmZmVyZW50IHBlbmFsdHkgcGFyYW1ldGVycyB0byBhc3N1cmUgeW91IGhhdmUgY2FwdHVyZWQgdGhlIGJlc3Qgb25lLiBJZiBub3QsIGFkanVzdCB0aGUgcGVuYWx0eS4gKEhJTlQ6IHVzZSB0aGUgYGF1dG9wbG90KClgIGZ1bmN0aW9uKS4gCgpgYGB7cn0KYXV0b3Bsb3QobGVuZGluZ19ibGVuZCkKYXV0b3Bsb3QobGVuZGluZ19ibGVuZCwgdHlwZT0gJ3dlaWdodHMnKQpgYGAKLSBXaGljaCBtb2RlbHMgYXJlIGNvbnRyaWJ1dGluZyBtb3N0PwpUaGUgb25lIHRoYXQgY29udHJpYnV0ZXMgdGhlIG1vc3Qgc2VlbXMgdG8gYmUgdGhlIGxvZ2lzdGljIHJlZ3Jlc3Npb24sIGZvbGxvd2VkIGJ5IHRoZSByYW5kb20gZm9yZXN0LiAKCgoxMy4gRml0IHRoZSBmaW5hbCBzdGFja2VkIG1vZGVsIHVzaW5nIGBmaXRfbWVtYmVycygpYC4gCgpgYGB7cn0KbGVuZGluZ19maW5hbF9zdGFjayA8LSBsZW5kaW5nX2JsZW5kICU+JSAKICBmaXRfbWVtYmVycygpCgpsZW5kaW5nX2ZpbmFsX3N0YWNrCmBgYAoKLSBBcHBseSB0aGUgbW9kZWwgdG8gdGhlIHRyYWluaW5nIGRhdGEuICAKCmBgYHtyfQojbGVuZGluZ19maXQgPC0gbGVuZGluZ19maW5hbF9zdGFjayAlPiUKI2ZpdChkYXRhID0gbGVuZGluZ190cmFpbmluZykKYGBgCgpgYGB7cn0KcHJlZHNfYWNjdXJhY3lfdHJhaW4gPC1sZW5kaW5nX3RyYWluaW5nICU+JSAKICBiaW5kX2NvbHMocHJlZGljdChsZW5kaW5nX2ZpbmFsX3N0YWNrLCAKICAgICAgICAgICAgbmV3X2RhdGE9IC4pKSAlPiUgCiAgYmluZF9jb2xzKHByZWRpY3QobGVuZGluZ19maW5hbF9zdGFjaywgCiAgICAgICAgICAgICAgICAgICAgbmV3X2RhdGEgPSAuLAogICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicHJvYiIpKQoKCgojcHJlZHM8LWNvbGxlY3RfcHJlZGljdGlvbnMocHJlZHNfYWNjdXJhY3lfdHJhaW4pIApgYGAKCmBgYHtyfQojQ29uZnVzaW9uIE1hdHJpeCAKbGVuZGluZ19tYXQ8LXByZWRzX2FjY3VyYWN5X3RyYWluICU+JQogIGNvbmZfbWF0KENsYXNzLCAucHJlZF9jbGFzcykgCgogCmxlbmRpbmdfbWF0IApzdW1tYXJ5KGxlbmRpbmdfbWF0KQpgYGAKCgotQ29tcHV0ZSB0aGUgYWNjdXJhY3ksIGNvbnN0cnVjdCBhIGNvbmZ1c2lvbiBtYXRyaXgsIGFuZCBjcmVhdGUgYSBkZW5zaXR5IHBsb3Qgd2l0aCBgLnByZWRfZ29vZGAgb24gdGhlIHgtYXhpcyAodGhlIHByb2JhYmlsaXR5IG9mIGEgcmVzcG9uc2Ugb2YgImdvb2QiKSwgZmlsbGVkIGJ5IGBDbGFzc2AuIENvbW1lbnQgb24gd2hhdCB5b3Ugc2VlLgoKCmBgYHtyfQpwcmVkc19hY2N1cmFjeV90cmFpbiU+JQogIGdncGxvdChhZXMoeCA9IC5wcmVkX2dvb2QsIGZpbGwgPSBDbGFzcykpKwogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuNSwgY29sb3IgPSBOQSkKYGBgClBvcmNlbnRhamUgZGVsIHRpZW1wbyBxdWUgcHJlZGljZSB1biBjYXNlIGNvbW8gYnVlbm8gdnMuIGVsIHBvcmNlbnRhamUgZGVsIHRpbWVwbyBxdWUgcHJlZGljZSB1biBjYXNlIGNvbW8gbWFsby4gCgoxNC4gSW4gdGhlIHByZXZpb3VzIHByb2JsZW0sIHlvdSBzYXcgdGhhdCBhbHRob3VnaCB0aGUgYWNjdXJhY3kgd2FzIHF1aXRlIGhpZ2gsIHRoZSB0cnVlIG5lZ2F0aXZlIHJhdGUgKGFrYSBzZW5zaXRpdml0eSkgd2FzIHRlcnJpYmxlLiBJdCdzIGNvbW1vbiB0byBzZWUgdGhpcyB3aGVuIG9uZSBvZiB0aGUgY2xhc3NlcyBoYXMgbG93IHJlcHJlc2VudGF0aW9uLiBXaGF0IHdlIHdhbnQgdG8gZG8gbm93IGlzIGludmVzdGlnYXRlIHdoYXQgaGFwcGVucyBpbiBlYWNoIG9mIG91ciBtb2RlbHMuIEJlbG93IEkndmUgcHJvdmlkZWQgY29kZSB0byBpbnZlc3RpZ2F0ZSB0aGUgbGFzc28gbW9kZWwgKHdoZXJlIGBsYXNzb190dW5lYCBpcyB0aGUgbmFtZSBvZiBteSB0dW5pbmcgc3RlcCkuIERvIHNpbWlsYXIgdGhpbmdzIGZvciB0aGUgcmFuZG9tIGZvcmVzdCBhbmQgeGdib29zdCBtb2RlbHMuCgpgYGB7ciwgZXZhbD1GQUxTRX0KbGFzc29fdHVuZSAlPiUgCiAgY29sbGVjdF9wcmVkaWN0aW9ucygpICU+JSAKICBncm91cF9ieShpZCwgcGVuYWx0eSkgJT4lIAogIHN1bW1hcml6ZShhY2N1cmFjeSA9IHN1bSgoQ2xhc3MgPT0gLnByZWRfY2xhc3MpKS9uKCksCiAgICAgICAgICAgIHRydWVfbmVnX3JhdGUgPSBzdW0oQ2xhc3MgPT0gImJhZCIgJiAucHJlZF9jbGFzcyA9PSAiYmFkIikvc3VtKENsYXNzID09ICJiYWQiKSwKICAgICAgICAgICAgdHJ1ZV9wb3NfcmF0ZSA9IHN1bShDbGFzcyA9PSAiZ29vZCIgJiAucHJlZF9jbGFzcyA9PSAiZ29vZCIpL3N1bShDbGFzcyA9PSAiZ29vZCIpKSAlPiUgCiAgZ3JvdXBfYnkocGVuYWx0eSkgJT4lIAogIHN1bW1hcml6ZShhY3Jvc3MoYWNjdXJhY3k6dHJ1ZV9wb3NfcmF0ZSwgbWVhbikpCmBgYAoKYGBge3J9CnJmX3R1bmUgJT4lIAogIGNvbGxlY3RfcHJlZGljdGlvbnMoKSAlPiUgCiAgZ3JvdXBfYnkoaWQpICU+JSAKICBzdW1tYXJpemUoYWNjdXJhY3kgPSBzdW0oKENsYXNzID09IC5wcmVkX2NsYXNzKSkvbigpLAogICAgICAgICAgICB0cnVlX25lZ19yYXRlID0gc3VtKENsYXNzID09ICJiYWQiICYgLnByZWRfY2xhc3MgPT0gImJhZCIpL3N1bShDbGFzcyA9PSAiYmFkIiksCiAgICAgICAgICAgIHRydWVfcG9zX3JhdGUgPSBzdW0oQ2xhc3MgPT0gImdvb2QiICYgLnByZWRfY2xhc3MgPT0gImdvb2QiKS9zdW0oQ2xhc3MgPT0gImdvb2QiKSkgJT4lIAogIHN1bW1hcml6ZShhY3Jvc3MoYWNjdXJhY3k6dHJ1ZV9wb3NfcmF0ZSwgbWVhbikpCmBgYAoKYGBge3J9CmJvb3N0X3R1bmUgJT4lIAogIGNvbGxlY3RfcHJlZGljdGlvbnMoKSAlPiUgCiAgZ3JvdXBfYnkoaWQpICU+JSAKICBzdW1tYXJpemUoYWNjdXJhY3kgPSBzdW0oKENsYXNzID09IC5wcmVkX2NsYXNzKSkvbigpLAogICAgICAgICAgICB0cnVlX25lZ19yYXRlID0gc3VtKENsYXNzID09ICJiYWQiICYgLnByZWRfY2xhc3MgPT0gImJhZCIpL3N1bShDbGFzcyA9PSAiYmFkIiksCiAgICAgICAgICAgIHRydWVfcG9zX3JhdGUgPSBzdW0oQ2xhc3MgPT0gImdvb2QiICYgLnByZWRfY2xhc3MgPT0gImdvb2QiKS9zdW0oQ2xhc3MgPT0gImdvb2QiKSkgJT4lIAogIHN1bW1hcml6ZShhY3Jvc3MoYWNjdXJhY3k6dHJ1ZV9wb3NfcmF0ZSwgbWVhbikpCmBgYApMQVNTTyAgaGFzIDcyJSBhY2N1cmFjeSwgbGFtYmRhPTcuNywgdHJ1ZSBuZWdhdGl2ZSByYXRlPSA2MyUJCgogSWYgeW91J2QgbGlrZSB0byBoYXZlIGEgYmV0dGVyIHRydWUgbmVnYXRpdmUgcmF0ZSwgd2hpY2ggbW9kZWxzIHdvdWxkIHlvdSBjaG9vc2UgYW5kIGhvdyB3b3VsZCB5b3UgZ28gYWJvdXQgZG9pbmcgdGhpcyBpbiBhIGxlc3MgbWFudWFsIHdheSAoeW91IGRvbid0IG5lZWQgdG8gd3JpdGUgY29kZSB0byBkbyBpdCAtIGp1c3QgZGVzY3JpYmUgaXQgaW4gd29yZHMpLiBCZSBzdXJlIHRvIHJlbW92ZSB0aGUgYGV2YWw9RkFMU0VgIHdoZW4geW91IGFyZSBmaW5pc2hlZC4KCiMjIFNoaW55IGFwcAoKRm9yIHRoaXMgd2VlaywgdGhlcmUgaXMgbm8gY29kZSB0byB0dXJuIGluIGZvciB0aGlzIHBhcnQuIFlvdSBhcmUganVzdCBnb2luZyB0byBuZWVkIHRvIHRoaW5rIGFib3V0IHRoZSBzdGVwcyB0byB0YWtlLgoKSWYgeW91IGFyZSBuZXcgdG8gU2hpbnkgYXBwcyBvciBpdCdzIGJlZW4gYXdoaWxlIHNpbmNlIHlvdSd2ZSBtYWRlIG9uZSwgdmlzaXQgdGhlIFNoaW55IGxpbmtzIG9uIG91ciBjb3Vyc2UgW1Jlc291cmNlXShodHRwczovL2FkdmFuY2VkLWRzLWluLXIubmV0bGlmeS5hcHAvcmVzb3VyY2VzLmh0bWwpIHBhZ2UuIEkgd291bGQgcmVjb21tZW5kIHN0YXJ0aW5nIHdpdGggbXkgcmVzb3VyY2UgYmVjYXVzZSBpdCB3aWxsIGJlIHRoZSBtb3N0IGJhc2ljLiAKCkV2ZXJ5b25lIHNob3VsZCB3YXRjaCB0aGUgW1RoZW1pbmcgU2hpbnldKGh0dHBzOi8veW91dHUuYmUvYjlXV05PNFAyblkpIHRhbGsgYnkgQ2Fyc29uIFNpZXZlcnQgc28geW91IGNhbiBtYWtlIHlvdXIgYXBwIGxvb2sgYW1hemluZy4KCioqVGFza3M6KioKCkluIHRoZSBmdXR1cmUsIHlvdSBhcmUgZ29pbmcgdG8gY3JlYXRlIGFuIGFwcCB0aGF0IGFsbG93cyBhIHVzZXIgdG8gZXhwbG9yZSBob3cgdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0eSBvZiBhIGxvYW4gYmVpbmcgcGFpZCBiYWNrIChvciBtYXliZSBqdXN0IHRoZSBwcmVkaWN0ZWQgY2xhc3MgLSBlaXRoZXIgImdvb2QiIG9yICJiYWQiKSBjaGFuZ2VzIGRlcGVuZGluZyBvbiB0aGUgdmFsdWVzIG9mIHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzLgoKRm9yIHRoaXMgd2VlaywgSSB3YW50IHlvdSB0byBhbnN3ZXIgdGhlIGZvbGxvd2luZyBxdWVzdGlvbnM6CgoxLiBIb3cgY2FuIHlvdSBzYXZlIGEgbW9kZWwgeW91IGJ1aWx0IHRvIHVzZSBpdCBsYXRlciAobGlrZSBpbiB0aGUgc2hpbnkgYXBwIHlvdSdsbCBjcmVhdGUpPwoKV2Ugd2lsbCB1c2UgdGhlIGZvbGxvd2luZyBjb2RlCnNhdmUobXlfbW9kZWwgLCBmaWxlID0gJ1N0YWNrTW9kZWwucmRhJykKCjIuIEZvciBzaGlueSBhcHBzIHRoYXQgZ2V0IHB1Ymxpc2hlZCAobGlrZSB5b3VycyB3aWxsKSwgaXQncyB2ZXJ5IGltcG9ydGFudCB0byBoYXZlIEFMTCB0aGUgbGlicmFyaWVzIHRoYXQgYXJlIHVzZWQgd2l0aGluIHRoZSBhcHAgbG9hZGVkLiBJZiB3ZSB3ZXJlIGdvaW5nIHRvIHVzZSB0aGUgc3RhY2tlZCBtb2RlbCwgd2hpY2ggbGlicmFyaWVzIGRvIHlvdSB0aGluayB3ZSdkIG5lZWQgdG8gbG9hZCBpbiBvdXIgYXBwPyAgCgpsaWJyYXJ5KHRpZHl2ZXJzZSksIGxpYnJhcnkodGlkeW1vZGVscyksIGxpYnJhcnkodGhlbWlzKSwgbGlicmFyeShkb1BhcmFsbGVsKSwgbGlicmFyeShzdGFja3MpLCBsaWJyYXJ5KG5hbmlhciksIGxpYnJhcnkobHVicmlkYXRlKSwgbGlicmFyeShtb2Rlcm5kaXZlKSwgbGlicmFyeSh2aXApLCBsaWJyYXJ5KHBhdGNod29yayksIHRoZW1lX3NldCh0aGVtZV9taW5pbWFsKCkpIAoKMy4gWW91J2xsIHdhbnQgdGhlIHVzZXIgdG8gYmUgYWJsZSB0byBjaG9vc2UgdmFsdWVzIGZvciBlYWNoIHZhcmlhYmxlIGluIHRoZSBtb2RlbC4gSG93IHdpbGwgeW91IGNvbWUgdXAgd2l0aCB0aGUgdmFsdWVzIHRoZXkgY2FuIGNob29zZSBmb3IgcXVhbnRpdGF0aXZlIGFuZCBjYXRlZ29yaWNhbCBkYXRhPyBHaXZlIG9uZSBleGFtcGxlIGZvciBlYWNoLCBlaXRoZXIgdXNpbmcgY29kZSBvciBpbiB3b3Jkcy4gIAoKRm9yIGVhY2ggcHJlZGljdG9yIEkgd291bGQgc2V0IGEgbnVtYmVyIG9yIHRleHQgaW5wdXQgd2hlcmUgcGVvcGxlIGNhbiBjaG9vc2UgdGhlaXIgdmFsdWVzLiAKCnVpIDwtIGZsdWlkUGFnZSgKICAgICAgdGV4dElucHV0KCAidmVyaWZpY2F0aW9uX3N0YXR1cyIsIAogICAgICAgICAgICAgICAgICJTdGF0dXMiLCAKICAgICAgICAgICAgICAgICB2YWx1ZSA9ICIiLCAKICAgICAgICAgICAgICAgICBwbGFjZWhvbGRlciA9ICJDaG9vc2UgeW91ciBzdGF0dXMiKSwKICAgICAgICAgICAgICAgICAKVGhlbiBJIHdvdWxkIGFkZCBhIHN1Ym1pdCBidXR0b24uIAoKSSB3b3VsZCBkbyB0aGUgc2FtZSB0aGluZyBidXQgd2l0aCBudW1lcmljIGlucHV0cy4gTWF5YmUgYWRkIGEgc3F1YXJlIHdoZXJlIHVzZXJzIGNhbiB0eXBlIHRoZSBudW1iZXIgdGhleSB3YW50IGZvciBjb250aW51b3VzIHZhcmlhYmxlcy4gCgo0LiBZb3Ugd2lsbCBuZWVkIHRvIHBvcHVsYXRlIGVhY2ggdmFyaWFibGUgd2l0aCBhbiBpbml0aWFsIHZhbHVlLiBXaGljaCB2YWx1ZSB3aWxsIHlvdSBjaG9vc2U/IElzIHRoZXJlIGEgbmljZSB3YXkgdG8gZG8gdGhpcyBwcm9ncmFtYXRpY2FsbHkgKGllLiB3aXRoIGNvZGUpPwoKSSB3aWxsIHdvcmsgb24gdGhpcyB3aXRoIExpc2EgZHVyaW5nIG9mZmljZSBob3VycyBiZWNhdXNlIEkgZ2VudWluZWx5IGRvbid0IGtub3cuIAoKCiMjIEZ1bmN0aW9uIEZyaWRheSBwcm9ibGVtcwoKSSB3aWxsIGxpbmsgdG8gdGhlc2Ugc2VwYXJhdGVseS4gVGhleSB3aWxsIGJlIHBvc3RlZCBieSBUdWVzZGF5LgoKIyMgQ29kZWQgQmlhcwoKV2Ugd2lsbCBiZSB3YXRjaGluZyBzb21lIG9mIHRoZSBbQ29kZWQgQmlhc10oaHR0cHM6Ly93d3cuY29kZWRiaWFzLmNvbS8pIGZpbG0gdG9nZXRoZXIgb24gVGh1cnNkYXkuIEl0IGlzIHN0cmVhbWluZyBvbiBOZXRmbGl4LiBXcml0ZSBhIHNob3J0IHJlZmxlY3Rpb24uIElmIHlvdSB3YW50IHNvbWUgcHJvbXB0cywgcmVmbGVjdCBvbjogV2hhdCBwYXJ0IG9mIHRoZSBmaWxtIGltcGFjdGVkIHlvdSB0aGUgbW9zdD8gV2FzIHRoZXJlIGEgcGFydCB0aGF0IHN1cnByaXNlZCB5b3UgYW5kIHdoeT8gV2hhdCBlbW90aW9ucyBkaWQgeW91IGV4cGVyaWVuY2Ugd2hpbGUgd2F0Y2hpbmc/CgogQWZ0ZXIgZm9yY2luZyBteSBob3VzZW1hdGVzIHRvIHdhdGNoIHRoZSBkb2N1bWVudGFyeSB3aXRoIG1lIG92ZXIgZGlubmVyLCBJIHRoaW5rIHdoYXQgaW1wYWN0ZWQgdXMgdGhlIG1vc3Qgd2FzIGltYWdpbmluZyB0aGUgcG9zc2liaWxpdHkgb2YgbGl2aW5nIGluIGEgc3RhdGUgb2YgZnVsbCBzdXJ2ZWlsbGFuY2UuIFdlIHRhbGtlZCBhYm91dCB0aGlzLCBhbmQgZXZlbiB0aG91Z2ggd2Uga25vdyB0aGF0IHdlIGFyZSBiZWluZyB0cmFja2VkIGFuZCBzcGllZCBvdmVyIHRoZSBjb25zdW1wdGlvbiB3ZSBtYWtlIG9ubGluZSBvciB0aGUgY29udGVudCB3ZSB3YXRjaCwgdGhpcyBpcyBkb25lIGxvdy1rZXkgYW5kIHdlIGFyZSBhYmxlIHRvIGlnbm9yZSBpdCwgb3IganVzdCBub3QgdGhpbmsgYWJvdXQgaXQuIElmIHdlIGltYWdpbmUgb3VyIGZhY2VzIGJlaW5nIHRyYWNrZWQgaW4gdGhlIHN0cmVldHMsIGxpa2UgaXQgaGFwcGVucyBpbiBMb25kb24gb3IgaW4gQ2hpbmEsIGl0IGdldHMgYSBiaXQgbW9yZSByZWFsIGFuZCBhbGwgb2YgdGhlIHN1ZGRlbiBpdCBpcyBzY2FyeS4gCiAgSXQgaXMgYWxzbyB2ZXJ5IHNjYXJ5IHRvIGZlZWwgdGhhdCBhbGdvcml0aG1zIGNvbnRyb2wgb3VyIGxpdmVzIGFuZCB0aGUgd2F5IHdlIG1ha2UgZGVjaXNpb25zIHRoYXQgYWZmZWN0IHRoZSBsaXZlcyBhbmQgZGVzdGluaWVzIG9mIG1pbGxpb25zIG9mIHBlb3BsZSBhcm91bmQgdGhlIHdvcmxkLiBGcm9tIGhpcmluZywgdG8gY3JlZGl0IGxvYW5zLCB0byBob3VzaW5nIG9wcG9ydHVuaXRpZXMsIHRoZXNlIGFsZ29yaXRobXMgYXJlIGJlaGluZCBtYW55IGNvbXBhbmllcyB0aGF0IGNvbnRyb2wgdGhlIHdheSB3ZSBsaXZlIGluIHNvY2lldHkgYW5kIHRoZXkgYXJlIGVtYmVkZGVkIHdpdGggdGhlIGlkZWFzIGFuZCBpZGVvbG9naWVzIG9mIHRoZSBwZXJzb24gdGhhdCBjcmVhdGVkIGl0IGFuZCB0aGUgZGF0YSBpdCB3YXMgZmVkIHdpdGguIEkgbG92ZSBob3cgdGhlIGRvY3VtZW50YXJ5IGV4cG9zZXMgdGhlIG1hbnkgYmlhc2VzIGJlaGluZCB0aGUgYWxnb3JpdGhtcyBhbmQgaG93IHRoZXkgZGVub3VuY2UgdGhlICJhbGdvcml0aG1pYyBoYXJtIiBhcyBhIHBvd2VyIHN0cnVjdHVyZSB3aGVyZSB3ZSAobm9ybWFsIHBlb3BsZSkgY2Fubm90IGFwcGVhbCB0byB0aGUgZGVjaXNpb25zIG1hZGUgdGhhdCBoYXJtIHVzLiBJIHRvdGFsbHkgYWdyZWUgd2l0aCB3aGF0IHdhcyBzYWlkIGFib3V0IHRoZSBmYWN0IHRoYXQgd2UgbmVlZCB0byBiZSBjb25zdGFudGx5IG1vbml0b3Jpbmcgb3VyIHVzZSBvZiBhbGdvcml0aG1zLiBUaGUgQmlnIEJyb3RoZXIgV2F0Y2ggZ3JvdXAgaW4gTG9uZG9uIGF0dGVtcHRzIHRvIGRvIHNvLCBidXQgdGhlcmUgc2hvdWxkIGJlIGEgcmVndWxhdG9yeSBib2R5IGNvbnRyb2xsaW5nIGhvdyBiaWcgY29tcGFuaWVzIHVzZSBhbGdvcml0aG1zIGluIHRoZWlyIGFjdGl2aXRpZXMsIGFzIHdlbGwuIFRoZSBzeXN0ZW1pYyBiaWFzZXMgdGhhdCBhbHJlYWR5IGV4aXN0IGluIG91ciB3b3JsZCwgZ2V0IHRyYW5zbGF0ZWQgdG8gdGhlIHRlY2hub2xvZ3kgZ292ZXJuaW5nIG91ciBsaXZlcyBhbmQgdGhhdCBpcyBzb21ldGhpbmcgcmVhbGx5IHdvcnJpc29tZSB0byBtZS4gCgpSRU1FTUJFUiBUTyBBREQgWU9VUiBHSVRIVUIgTElOSyBBVCBUSEUgVE9QIE9GIFRIRSBQQUdFIEFORCBVTkNPTU1FTlQgVEhFIGBrbml0cmAgT1BUSU9OUy4KCgo=